#include "lv_driver.h"

#include <Arduino.h>
 
#include <Ticker.h>
#include "FT6336.h"
#include <TFT_eSPI.h>
#include <SPI.h>
#include <Wire.h>

#include "GxEPD2_BW.h"
#include "PCA9554.h"//0X20 SLAVE
#include "RTClib.h"

 

 
PCF8563 rtc;

PCA9554 io(0x20);  // Create an object at this address
TFT_eSPI tft = TFT_eSPI();  // Invoke library, pins defined in User_Setup.h
GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> eink(GxEPD2_270(/*CS=*/ 15, /*DC=*/ 12, /*RST=*/ 5, /*BUSY=*/ 13));
FT6336_Class ctp;
TP_Point point;
Ticker tick1; /* timer for interrupt handler */
Ticker tick2; /* timer for interrupt handler */
bool eink_flash_ready=false;//eink是否刷新完成
bool eink_scene_ready = false;//新的界面是否刷新完成

lv_disp_t * lv_tft;
lv_disp_t * lv_eink;

static lv_disp_buf_t disp_buf_tft;
static lv_color_t buf_tft[160*80];

static lv_disp_buf_t disp_buf_eink;
static lv_color_t buf_eink[264*176/8];


static void iic_test(void);

 
static void tft_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
static void eink_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
static void eink_monitor_cb(lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px);
static void eink_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
static bool touchpad_read(lv_indev_drv_t * indev, lv_indev_data_t * data) ;

void lv_driver_init(void)
{
 
    Wire.begin();
    rtc.begin();
    io.portMode0(0xc4);// //1100 0100 
    io.digitalWrite0(0,0);
    ctp.begin();
    io.digitalWrite0(3, 1);//   //TFT_CS//   //TFT_CS

    eink.init(115200);     
    eink.setRotation(0);
    // eink.setFont(&FreeMonoBold9pt7b);
    eink.setTextColor(GxEPD_BLACK);
    eink.setFullWindow();
    eink.firstPage();
    do
    {
        eink.fillScreen(GxEPD_WHITE);        
    }
    while (eink.nextPage());



    io.digitalWrite0(3, 0);//   //TFT_CS//   //TFT_CS
    tft.init();
    tft.setRotation(1);
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_GREENYELLOW, TFT_BLACK);
    tft.drawCentreString("Jakiro",80,30,4);
    tft.drawRightString("v1.1",150,55,2);
    io.digitalWrite0(3, 1);//   //TFT_CS//   //TFT_CS

    delay(1000);

    iic_test();
    delay(1000);


    lv_disp_buf_init(&disp_buf_tft, buf_tft, NULL, 160 * 80);
    lv_disp_buf_init(&disp_buf_eink, buf_eink, NULL, 176*264/8);

    lv_disp_drv_t disp_drv_eink;
    lv_disp_drv_t disp_drv_tft;

    lv_disp_drv_init(&disp_drv_tft);
    disp_drv_tft.hor_res = 160;
    disp_drv_tft.ver_res = 80;
    disp_drv_tft.flush_cb = tft_disp_flush;
    disp_drv_tft.buffer = &disp_buf_tft;
    lv_tft = lv_disp_drv_register(&disp_drv_tft);

    /*Initialize the display*/

    lv_disp_drv_init(&disp_drv_eink);
    disp_drv_eink.hor_res = 176;
    disp_drv_eink.ver_res = 264;
    disp_drv_eink.flush_cb = eink_disp_flush;
    disp_drv_eink.buffer = &disp_buf_eink;
    disp_drv_eink.monitor_cb = eink_monitor_cb;
    disp_drv_eink.set_px_cb = NULL;
    lv_eink = lv_disp_drv_register(&disp_drv_eink);


    /*Initialize the touch pad*/
    
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    indev_drv.disp = lv_eink;
    lv_indev_drv_register(&indev_drv);

    tick1.attach_ms(1, []{
      lv_tick_inc(1);
    });
    tick2.attach_ms(5, []{
      lv_task_handler();
    });

}

//tft
static void tft_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  digitalWrite(14,1);//sd cs high 
  digitalWrite(15,1);//eink cs high
  uint16_t c;
  io.digitalWrite0(3,0);
  SPI.setFrequency(27000000);
  tft.startWrite(); /* Start new TFT transaction */
  tft.setAddrWindow(area->x1, area->y1, (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1)); /* set the working window */
  for (int y = area->y1; y <= area->y2; y++) {
    for (int x = area->x1; x <= area->x2; x++) {
      c = color_p->full;
      tft.writeColor(c, 1);
      color_p++;
    }
  }
  tft.endWrite(); /* terminate TFT transaction */
  io.digitalWrite0(3,1);
  lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
   
 
}



//eink
static void eink_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    if(eink_flash_ready)
    {

    }
    else
    {
       digitalWrite(14,1);//sd cs high 
        io.digitalWrite0(3, 1);//   //TFT_CS//   //TFT_CS 
        
        eink.setPartialWindow(0, 0, eink.width(), eink.height());// (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1));
        // eink.setPartialWindow(area->x1, area->y1,  (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1));
        // eink.setRotation(0); 
        // eink.setTextColor(GxEPD_BLACK);
    
        //只是清理eink的buf,不会立马显示到桌面上,由于设置了显示区域,因此只会更新显示区域内的东西到屏幕上,
        //当然也可以不用,毕竟需要花费时间
        //调用fillScreen
        //  eink.firstPage();
        // do
        {
        // eink.fillScreen(GxEPD_WHITE); //对buff赋值
          for (int y = area->y1; y <= area->y2; y++) 
          {
              for (int x = area->x1; x <= area->x2; x++)  
              {   
              if(color_p->full!=0xffff)
              {
                  eink.drawPixel(x,y,GxEPD_BLACK);
              }
              else
              {
                  
                  eink.drawPixel(x,y,GxEPD_WHITE);
              }
              color_p++;   
              // Serial.printf("%04x ",color_p->full); 
              }

          }
        }
        //while (eink.nextPage());
    }
    lv_disp_flush_ready(disp); // tell lvgl that flushing is done 
    // Serial.println("eink");
 
}


 /**
 * Called when a the library finished rendering to monitor its performance
 * @param disp_drv pointer to the caller display driver
 * @param time_ms time of rendering in milliseconds
 * @param px_num Number of pixels drawn
 */
static void eink_monitor_cb(lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px)
{
  SPI.setFrequency(10000000);
    // eink.refresh(0,0,176,264);
    // printf("%d px refreshed in %d ms\n", px,time);
    
    eink_flash_ready = true;
    if(eink_scene_ready==false)
    {
      while (eink.nextPage());
      eink_scene_ready=true;
    }
 
}

/* Return the byte bitmask of a pixel color corresponding to VDB arrangement */
#define PIXIDX(y, c)	((c) << (((y) & 1) << 1))
/** OPTIONAL: Set a pixel in a buffer according to the special requirements of the display
     * Can be used for color format not supported in LittelvGL. E.g. 2 bit -> 4 gray scales
     * @note Much slower then drawing with supported color formats. */
static void eink_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
   (void) disp_drv;
  (void) opa;
  buf += buf_w * (y >> 3) + x;
 Serial.printf("%d,%d ",x,y);
 
 
}


/* Reading input device  */
static bool touchpad_read(lv_indev_drv_t * indev, lv_indev_data_t * data) 
{
  static lv_coord_t last_x = 0;
  static lv_coord_t last_y = 0;
  uint16_t x, y;
  /*Save the state and save the pressed cooridnate*/
  if (ctp.touched() )
  {
    point = ctp.getPoint();
    data->state = LV_INDEV_STATE_PR;
    last_x = point.x;
    last_y = point.y;
  } else {
    data->state = LV_INDEV_STATE_REL;
  }
  /*Set the coordinates (if released use the last pressed cooridantes)*/
  data->point.x = last_x;
  data->point.y = last_y;

  return false; /*Return `false` because we are not buffering and no more data to read*/
}


 




static void iic_test(void)
{
  io.digitalWrite0(3,0);
  tft.fillScreen(TFT_BLACK);
    for(uint8_t address = 1; address < 127; address++ ) 
  {
    Wire.beginTransmission(address);
    int error = Wire.endTransmission();
    if(error==0)
    {
      tft.print(address,HEX);tft.print(" ");
    }
    // else tft.print("_");

    delay(10);
  }
  // Serial.println("_");
  io.digitalWrite0(3,1);
 
// delay(2000);

}